package com.xuqi.DownLoad;

import com.xuqi.W3.W3;
import com.xuqi.W3.W3Request;

import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.util.Log;

public class W3DownloadControl implements W3.IW3DownloadEventListener
{
	// post to main thread
	public static final int DOWNLOAD_FINISHED = 0x20000000;
	public static final int DOWNLOAD_PROGRESS = 0x20000001;
	public static final int DOWNLOAD_BEGIN = 0x20000003;
	public static final int DOWNLOAD_TOTALSIZE = 0x20000004;

	// post to download thread
	private final int REQUESTDOWNLOAD = 0x0010;

	// post message from download thread to main thread
	private Handler msgHandler = null;

	private W3 connect = null;
	private W3Request downParam = null;

	private DownloadThread downloadThread = null;

	private DownloadCallBack mCallBack = null;

	// to indicate whether a download is running,
	// to protect from request new one until the running is ended
	private boolean mDownloading = false;

	public W3DownloadControl()
	{

	}

	public void init()
	{
		connect = new W3();
		connect.initW3();

		connect.setW3DownloadProgressListener(this);
		downParam = new W3Request();
		downParam.setMethod(W3Request.W3_REQ_METHOD_GET);

		msgHandler = new MainHandler();

		downloadThread = new DownloadThread("downloadThread");
		downloadThread.start();
	}

	// set from user to declare who to handle message from downloadThread
	public void setCallBack(DownloadCallBack callback)
	{
		Log.d("W3DownloadControl", "setCallBack" + callback);
		mCallBack = callback;
	}

	// if downloading, cancel it
	public void cancel(boolean bCancel)
	{
		if (connect != null)
		{
			connect.setCancelFlag(bCancel);
		}
	}

	public boolean isCancel()
	{
		if (connect != null)
		{
			return connect.getCancelFlag();
		}
		return false;
	}

	/*
	 * to start downloading file in a new Thread, when downloading finished,
	 * post a message to msgHandler.
	 * 
	 * params: url: file's web address in http or https protocol fileFullPath:
	 * file to store data downloaded, must be has effect path(parent dir must be
	 * available).
	 */

	public boolean downloadFile(String url, String fileFullPath)
	{
		return downloadFile(url, null, fileFullPath, null);
	}

	/*
	 * to start downloading file in a new Thread, when downloading finished,
	 * post a message to msgHandler.
	 * 
	 * params: url: file's web address in http or https protocol header: request
	 * header, could be null fileFullPath: file to store data downloaded, must
	 * be has effect path(parent dir must be available). it could be null, which
	 * means to download data to memory buffer, whose size is set by
	 * "setDataBufferSize". bodyMsg: to store response data's body message, it
	 * could be null headerFlag: to set cookies, content type etc. default is 0.
	 */

	public boolean downloadFile(String url, String header, String fileFullPath, byte[] bodyMsg)
	{
		Log.d("W3DownloadControl", Thread.currentThread().getName() + "\t downloadFile\t" + url);
		//
		// if (_isDownloading()) {
		// // TODO: if is downloading, return false, ignore it? or add to queue?
		// return false;
		// }

		if (msgHandler == null)
		{
			Log.w("W3DownloadControl", "Not set message handler, no progress message sent");
			msgHandler = new MainHandler();
		}

		downParam.setURL(url);
		downParam.setSendHeaders(header);
		if (fileFullPath == null)
		{
			downParam.setReceiveDataToBuffer();
		}
		else
		{
			downParam.setReceiveFileName(fileFullPath);
		}

		downParam.setSendData(bodyMsg);

		if (downloadThread == null)
		{
			downloadThread = new DownloadThread("downloadThread");
			downloadThread.start();
		}

		if (downloadThread != null)
		{
			Handler handler = downloadThread.getHandler();
			if (handler != null)
			{
				Message request = handler.obtainMessage(REQUESTDOWNLOAD);
				handler.sendMessage(request);
			}
		}

		return true;
	}

	// Get the response data
	public byte[] GetResponseData()
	{
		if (connect != null && connect.getResponse() != null)
			return connect.getResponse().getResponseData();

		return null;
	}

	// Change the buffer size. The default is 50K.
	public void SetBufSize(int iBufSize)
	{
		throw new UnsupportedOperationException();
	}

	private synchronized void _setDownloading(boolean downloading)
	{
		mDownloading = downloading;
	}

	private synchronized boolean _isDownloading()
	{
		return mDownloading;
	}

	private boolean _downloadFile()
	{
		Log.d("W3DownloadControl", Thread.currentThread().getName() + "\t ThreadRunnable");

		if (connect != null && downParam != null)
		{
			String fileName = _parseFileName(downParam.getURL());
			Log.d("W3DownloadControl", "Send DOWNLOAD_BEGIN\t" + fileName);
			_sendMessgage2Main(DOWNLOAD_BEGIN, fileName, 0);
			_setDownloading(true);

			int result = connect.doW3Action(downParam);

			_setDownloading(false);

			Log.d("W3DownloadControl", "Send DOWNLOAD_FINISHED\t" + fileName + (result == 0 ? "\tsuccessful" : "\tfailed"));
			_sendMessgage2Main(DOWNLOAD_FINISHED, fileName, result);
			fileName = null;
			if (result == W3.W3_ERR_NO_ERROR)
				return true;
		}

		return false;
	}

	private void _sendMessgage2Main(int type, String fileName, int result)
	{
		if (msgHandler != null)
		{
			Message msg = msgHandler.obtainMessage(type, DownLoadMessage.createOne(fileName, result));
			msgHandler.sendMessage(msg);
		}
	}

	public void deInit()
	{

		_setDownloading(false);

		if (connect != null)
		{
			connect.setCancelFlag(true);
			connect.setW3DownloadProgressListener(null);
			connect.clearW3();
			connect = null;
		}

		if (msgHandler != null)
		{
			msgHandler.removeMessages(DOWNLOAD_FINISHED);
			msgHandler.removeMessages(DOWNLOAD_PROGRESS);
			msgHandler = null;
		}

		downParam = null;
		mCallBack = null;
		if (downloadThread != null)
		{
			downloadThread.quit();
		}

		downloadThread = null;
		// mSingleLock = null;

	}

	private String _parseFileName(final String url)
	{
		if (url == null)
			return null;
		int last = url.lastIndexOf("/");
		return url.substring(last + 1);
	}

	public interface DownloadCallBack
	{
		public abstract void downloadStart(String fileName);

		public abstract void downloadTotalSize(int totalsize);

		public abstract void downloadProgress(String fileName, int progress);

		public abstract void downloadFinished(String fileName, int result);
	}

	public static class DownLoadMessage
	{
		public DownLoadMessage(String file, int rt)
		{
			downLoadFile = file;
			result = rt;
		}

		// file's name
		public String downLoadFile = null;
		// when DOWNLOAD_FINISHED, it means return error of httpconnection
		// when DOWNLOAD_PROGRESS, it means size of data downloaded
		public int result = 0;

		public static DownLoadMessage createOne(String file, int result)
		{
			return new DownLoadMessage(file, result);
		}
	}

	private class DownloadThread extends HandlerThread
	{
		private DownloadHandler mHandler = null;

		public DownloadThread(String name)
		{
			super(name);
		}

		@Override
		protected void onLooperPrepared()
		{
			mHandler = new DownloadHandler();
			super.onLooperPrepared();
		}

		public Handler getHandler()
		{
			if (mHandler == null)
			{
				while (mHandler == null)
				{
					try
					{
						Log.w("W3DownloadControl", "Sleep 50 to wait DownloadThread started");
						Thread.sleep(50);
					} catch (InterruptedException e)
					{
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
			return mHandler;
		}

		@Override
		public boolean quit()
		{
			Log.w("DownloadThread", "DownloadThread quit\t");
			return super.quit();
		}
	}

	private class DownloadHandler extends Handler
	{
		public DownloadHandler()
		{
			super(Looper.myLooper());
		}

		@Override
		public void handleMessage(Message msg)
		{
			if (msg.what == REQUESTDOWNLOAD)
			{
				// only to download one, do not care result
				_downloadFile();
			}
		}
	}

	private class MainHandler extends Handler
	{
		public MainHandler()
		{
			super(Looper.getMainLooper());
		}

		@Override
		public void handleMessage(Message msg)
		{
			DownLoadMessage msgobj = (DownLoadMessage) msg.obj;
			if (msg.what == DOWNLOAD_FINISHED)
			{
				if (mCallBack != null)
				{
					Log.d("W3DownloadControl", "DOWNLOAD_FINISHED" + msgobj.downLoadFile + "\t" + (msgobj.result == W3.W3_ERR_NO_ERROR));
					mCallBack.downloadFinished(msgobj.downLoadFile, msgobj.result);
				}
			}
			else if (msg.what == DOWNLOAD_PROGRESS)
			{
				if (mCallBack != null)
				{
					mCallBack.downloadProgress(msgobj.downLoadFile, msgobj.result);
				}
			}
			else if (msg.what == DOWNLOAD_BEGIN)
			{
				if (mCallBack != null)
				{
					Log.d("W3DownloadControl", "DOWNLOAD_BEGIN" + msgobj.downLoadFile);
					mCallBack.downloadStart(msgobj.downLoadFile);
				}
			}
			else if (msg.what == DOWNLOAD_TOTALSIZE)
			{
				if (mCallBack != null)
				{
					Log.d("W3DownloadControl", "DOWNLOAD_TOTALSIZE" + msgobj.result);
					mCallBack.downloadTotalSize(msgobj.result);
				}
			}

		}
	}

	@Override
	public void onDownloadProgress(String strFileName, int iFileNowSize)
	{
		// Log.d("W3DownloadThread", "Downloaded\t" + progress + "\tbytes"
		// + "\tof\t" + _parseFileName(fileName));
		_sendMessgage2Main(DOWNLOAD_PROGRESS, _parseFileName(strFileName), iFileNowSize);
	}

	@Override
	public void onRecvHeader(int httpStatus, int expectedLength, String strMIME, String strEncoding)
	{
		Log.d("W3DownloadControl", "Downloaded totalsize\t" + expectedLength + "\tbytes");
		_sendMessgage2Main(DOWNLOAD_TOTALSIZE, "UNKNOWN", expectedLength);

	}
}